(() => {
  const canvas = document.getElementById('canvas');
  const ctx = canvas.getContext('2d');

  const ui = {
    modeButtons: Array.from(document.querySelectorAll('.controls button[data-mode]')),
    toggle: document.getElementById('toggle'),
    confessInput: document.getElementById('confessText'),
    showMsg: document.getElementById('showMsg'),
    hideMsg: document.getElementById('hideMsg'),
    overlay: document.getElementById('overlay'),
    overlayText: document.getElementById('overlayText'),
    shareLink: document.getElementById('shareLink'),
    downloadShot: document.getElementById('downloadShot'),
  };

  let width = 0, height = 0, dpr = Math.max(1, Math.min(window.devicePixelRatio || 1, 2));
  function resize() {
    width = window.innerWidth;
    height = window.innerHeight;
    canvas.style.width = width + 'px';
    canvas.style.height = height + 'px';
    canvas.width = Math.floor(width * dpr);
    canvas.height = Math.floor(height * dpr);
    ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
  }
  resize();
  window.addEventListener('resize', resize);

  // State
  let running = true;
  let mode = 'classic'; // 'classic' | 'heart' | 'emoji' | 'pro' | 'tsp'
  const emojis = ['❤️','💖','✨','🌟','💘','🎆','🧡','💫'];

  // Physics settings
  const gravity = 0.06;
  const drag = 0.985;
  const particleCountClassic = 130; // 增强爆炸规模
  const particleCountHeart = 120;
  const particleCountEmoji = 50;

  // Utility
  const rand = (min, max) => Math.random() * (max - min) + min;
  const randInt = (min, max) => Math.floor(rand(min, max));
  const hue = () => randInt(0, 360);
  const color = (h, s = 70, l = 60) => `hsl(${h}, ${s}%, ${l}%)`;

  // Trail fade
  function fadeBackground() {
    // 稍微降低覆盖透明度以保留更多拖尾
    ctx.fillStyle = 'rgba(11,16,35,0.18)';
    ctx.fillRect(0, 0, width, height);
  }

  class Particle {
    constructor(x, y, vx, vy, col, size = 2.4, ttl = 80, type = 'dot', text = '') {
      this.x = x; this.y = y;
      this.vx = vx; this.vy = vy;
      this.col = col;
      this.size = size;
      this.ttl = ttl;
      this.life = 0;
      this.type = type; // 'dot' | 'emoji'
      this.text = text;
    }
    update() {
      this.vx *= drag;
      this.vy *= drag;
      this.vy += gravity;
      this.x += this.vx;
      this.y += this.vy;
      this.life++;
    }
    draw(ctx) {
      const alpha = Math.max(0, 1 - this.life / this.ttl);
      if (this.type === 'emoji') {
        ctx.save();
        ctx.globalAlpha = alpha;
        ctx.font = `${Math.max(16, this.size * 10)}px Apple Color Emoji, Noto Color Emoji, Segoe UI Emoji`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        ctx.fillText(this.text || '✨', this.x, this.y);
        ctx.restore();
        return;
      }
      // 叠加发光增强
      ctx.save();
      ctx.globalCompositeOperation = 'lighter';
      ctx.globalAlpha = alpha;
      ctx.shadowColor = this.col;
      ctx.shadowBlur = 12;
      ctx.fillStyle = this.col;
      ctx.beginPath();
      ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2);
      ctx.fill();
      ctx.restore();
    }
    get dead() { return this.life >= this.ttl; }
  }

  const particles = [];

  // --- fireworks-js 集成 ---
  let fwjs = {
    loaded: false,
    instance: null,
    container: document.getElementById('fwjs')
  };

  function loadFireworksJS(cb) {
    if (fwjs.loaded) return cb && cb();
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/fireworks-js@2/dist/index.umd.js';
    script.async = true;
    script.onload = () => { fwjs.loaded = true; cb && cb(); };
    document.head.appendChild(script);
  }

  function startFWJS() {
    if (!fwjs.loaded) return loadFireworksJS(startFWJS);
    if (!fwjs.instance) {
      const FireworksLib = window.Fireworks?.default || window.Fireworks;
      fwjs.instance = new FireworksLib(fwjs.container, {
        autoresize: true,
        opacity: 0.6,
        acceleration: 1.05,
        friction: 0.97,
        gravity: 1.5,
        particles: 120,
        traceLength: 5,
        traceSpeed: 12,
        explosion: 6,
        intensity: 45,
        flickering: 60,
        lineStyle: 'round',
        hue: { min: 0, max: 360 },
        delay: { min: 20, max: 45 },
        rocketsPoint: { min: 0, max: 100 },
        lineWidth: {
          explosion: { min: 2, max: 3 },
          trace: { min: 1, max: 2 }
        },
        brightness: { min: 55, max: 85 },
        decay: { min: 0.012, max: 0.028 },
        mouse: { click: true, move: false, max: 2 }
      });
    }
    fwjs.container.style.display = 'block';
    fwjs.instance.start();
  }

  function stopFWJS() {
    if (fwjs.instance) {
      fwjs.instance.stop(true);
      fwjs.container.style.display = 'none';
    }
  }

  // --- tsParticles fireworks 预设集成 ---
  const tsp = {
    loaded: false,
    container: document.getElementById('tsparticles'),
    instance: null,
  };

  function loadTsParticlesPreset(cb) {
    if (tsp.loaded) return cb && cb();
    const script = document.createElement('script');
    script.src = 'https://cdn.jsdelivr.net/npm/@tsparticles/preset-fireworks@3/tsparticles.preset.fireworks.bundle.min.js';
    script.async = true;
    script.onload = () => { tsp.loaded = true; cb && cb(); };
    document.head.appendChild(script);
  }

  async function startTSP() {
    if (!tsp.loaded) return loadTsParticlesPreset(startTSP);
    tsp.container.style.display = 'block';
    try {
      // tsParticles 来自 bundle 脚本
      if (!window.tsParticles) return;
      // 加载 fireworks 预设并启动
      if (window.loadFireworksPreset) {
        await window.loadFireworksPreset(window.tsParticles);
      }
      tsp.instance = await window.tsParticles.load({
        id: 'tsparticles',
        options: {
          preset: 'fireworks',
          background: { color: 'transparent' },
        },
      });
    } catch (e) {
      console.error('tsParticles fireworks 初始化失败', e);
    }
  }

  function stopTSP() {
    try {
      if (tsp.instance) {
        tsp.instance.destroy();
        tsp.instance = null;
      }
      tsp.container.style.display = 'none';
    } catch (e) { /* ignore */ }
  }

  // Heart shape sampling using classic parametric equation
  function heartPoints(n, scale = 8) {
    const pts = [];
    for (let i = 0; i < n; i++) {
      const t = Math.PI * 2 * (i / n);
      const x = 16 * Math.pow(Math.sin(t), 3);
      const y = 13 * Math.cos(t) - 5 * Math.cos(2 * t) - 2 * Math.cos(3 * t) - Math.cos(4 * t);
      pts.push({ x: x * scale, y: -y * scale });
    }
    return pts;
  }

  // Explosions
  function explodeClassic(x, y) {
    const h = hue();
    for (let i = 0; i < particleCountClassic; i++) {
      const angle = Math.random() * Math.PI * 2;
      const speed = rand(2.6, 6.4);
      const vx = Math.cos(angle) * speed;
      const vy = Math.sin(angle) * speed;
      particles.push(new Particle(x, y, vx, vy, color(h, 85, randInt(58,72)), rand(1.8, 3.0), randInt(70, 110)));
    }
    // 中心小型二次爆炸，提升视觉冲击
    if (Math.random() < 0.6) {
      const h2 = (h + randInt(20, 60)) % 360;
      for (let i = 0; i < 22; i++) {
        const angle = Math.random() * Math.PI * 2;
        const speed = rand(1.3, 2.1);
        const vx = Math.cos(angle) * speed;
        const vy = Math.sin(angle) * speed;
        particles.push(new Particle(x, y, vx, vy, color(h2, 90, randInt(65,80)), rand(1.5, 2.2), randInt(55, 85)));
      }
    }
  }

  function explodeHeart(x, y) {
    const h = hue();
    const pts = heartPoints(particleCountHeart, rand(4, 7));
    const jitter = 0.9;
    for (const p of pts) {
      const vx = (p.x + rand(-jitter, jitter)) / 18;
      const vy = (p.y + rand(-jitter, jitter)) / 18;
      particles.push(new Particle(x, y, vx, vy, color(h, 80, randInt(50,70)), rand(1.7, 2.6), randInt(70, 110)));
    }
  }

  function explodeEmoji(x, y) {
    const chosen = emojis[randInt(0, emojis.length)];
    const h = hue();
    for (let i = 0; i < particleCountEmoji; i++) {
      const angle = Math.random() * Math.PI * 2;
      const speed = rand(1.6, 3.6);
      const vx = Math.cos(angle) * speed;
      const vy = Math.sin(angle) * speed;
      const size = rand(2.2, 3.2);
      const ttl = randInt(65, 95);
      const p = new Particle(x, y, vx, vy, color(h), size, ttl, 'emoji', chosen);
      particles.push(p);
    }
  }

  function explode(x, y) {
    switch (mode) {
      case 'heart': return explodeHeart(x, y);
      case 'emoji': return explodeEmoji(x, y);
      default: return explodeClassic(x, y);
    }
  }

  // Auto rockets
  let spawnTimer = 0;
  function autoLaunch(delta) {
    spawnTimer -= delta;
    if (spawnTimer <= 0) {
      spawnTimer = rand(420, 900); // ms
      const startX = rand(0.15 * width, 0.85 * width);
      const targetY = rand(0.15 * height, 0.45 * height);
      const speed = rand(7.5, 9.5);
      const vx = rand(-0.8, 0.8);
      let x = startX, y = height + 10, vy = -speed;
      const steps = randInt(28, 36);
      // create a simple rocket animation that explodes at target
      const trailCol = color(hue(), 90, 70);
      for (let i = 0; i < steps; i++) {
        setTimeout(() => {
          if (!running) return;
          x += vx; y += vy; vy += gravity * 0.5;
          // trail particle（使用 lighter 叠加产生拖尾光辉）
          particles.push(new Particle(x, y, 0, 0, trailCol, 1.8, 36));
          if (i === steps - 1 || y <= targetY) explode(x, y);
        }, i * 16);
      }
    }
  }

  // Input handlers
  function setMode(newMode) {
    // 切换前的清理
    if (mode === 'pro') stopFWJS();
    if (mode === 'tsp') stopTSP();
    if (newMode === 'pro') {
      running = false; // 暂停自定义渲染循环
      ui.toggle.textContent = '继续';
      canvas.style.display = 'none';
      startFWJS();
    } else if (newMode === 'tsp') {
      running = false;
      ui.toggle.textContent = '继续';
      canvas.style.display = 'none';
      startTSP();
    } else {
      canvas.style.display = 'block';
      running = true;
      requestAnimationFrame(loop);
    }
    mode = newMode;
    ui.modeButtons.forEach(b => b.classList.toggle('active', b.dataset.mode === mode));
  }
  ui.modeButtons.forEach(b => b.addEventListener('click', () => setMode(b.dataset.mode)));
  ui.toggle.addEventListener('click', () => {
    if (mode === 'pro') {
      if (!fwjs.instance) return;
      fwjs.instance.pause();
      // fireworks-js 的 pause 是切换；通过 container 可判断可视状态
      // 这里同步按钮文本：根据 display 状态不可靠，直接翻转文本
      ui.toggle.textContent = ui.toggle.textContent === '暂停' ? '继续' : '暂停';
    } else if (mode === 'tsp') {
      if (!tsp.instance) return;
      // tsParticles 支持 pause/play
      const paused = tsp.instance.paused ?? false;
      if (paused) tsp.instance.play(); else tsp.instance.pause();
      ui.toggle.textContent = paused ? '暂停' : '继续';
    } else {
      running = !running;
      ui.toggle.textContent = running ? '暂停' : '继续';
      if (running) lastTs = performance.now();
      requestAnimationFrame(loop);
    }
  });

  ui.showMsg.addEventListener('click', () => {
    const text = (ui.confessInput.value || '').trim();
    ui.overlayText.textContent = text || '爱你到天荒地老';
    ui.overlay.classList.remove('hidden');
  });
  ui.hideMsg.addEventListener('click', () => ui.overlay.classList.add('hidden'));

  function pointerExplosion(e) {
    const rect = canvas.getBoundingClientRect();
    const x = (e.clientX || (e.touches && e.touches[0].clientX)) - rect.left;
    const y = (e.clientY || (e.touches && e.touches[0].clientY)) - rect.top;
    explode(x, y);
  }
  function attachPointerHandlers() {
    window.addEventListener('click', pointerExplosion);
    window.addEventListener('touchstart', pointerExplosion, { passive: true });
  }
  function detachPointerHandlers() {
    window.removeEventListener('click', pointerExplosion);
    window.removeEventListener('touchstart', pointerExplosion, { passive: true });
  }
  attachPointerHandlers();

  // 根据模式切换点击爆炸处理（专业模式交给 fireworks-js）
  ui.modeButtons.forEach(b => b.addEventListener('click', () => {
    const m = b.dataset.mode;
    if (m === 'pro' || m === 'tsp') detachPointerHandlers(); else attachPointerHandlers();
  }));

  // Main loop
  let lastTs = performance.now();
  function loop(ts) {
    if (!running) return;
    const delta = ts - lastTs; // ms
    lastTs = ts;
    fadeBackground();
    autoLaunch(delta);
    for (let i = particles.length - 1; i >= 0; i--) {
      const p = particles[i];
      p.update();
      p.draw(ctx);
      if (p.dead) particles.splice(i, 1);
    }
    requestAnimationFrame(loop);
  }
  requestAnimationFrame(loop);

  // initial mode
  setMode('classic');

  // --- URL 参数解析：预置模式与表白语 ---
  (function initFromURL(){
    try {
      const params = new URLSearchParams(location.search);
      const text = (params.get('text') || '').trim();
      const m = (params.get('mode') || '').trim();
      if (text) {
        ui.confessInput.value = text;
        ui.overlayText.textContent = text;
        ui.overlay.classList.remove('hidden');
      }
      const validModes = ['classic','heart','emoji','pro','tsp'];
      if (m && validModes.includes(m)) {
        setMode(m);
      }
    } catch(e) { /* ignore */ }
  })();

  // --- 分享链接：复制到剪贴板 ---
  ui.shareLink.addEventListener('click', async () => {
    const text = (ui.confessInput.value || '').trim();
    const params = new URLSearchParams();
    params.set('mode', mode);
    if (text) params.set('text', text);
    const url = `${location.origin}${location.pathname}?${params.toString()}`;
    try {
      await navigator.clipboard.writeText(url);
      const old = ui.shareLink.textContent;
      ui.shareLink.textContent = '已复制';
      setTimeout(() => ui.shareLink.textContent = old, 1200);
    } catch (err) {
      // 兼容不支持 clipboard 的环境
      const ta = document.createElement('textarea');
      ta.value = url;
      document.body.appendChild(ta);
      ta.select();
      try { document.execCommand('copy'); } catch(e){}
      document.body.removeChild(ta);
      alert('已生成分享链接\n请注意不要在公共网络环境分享隐私信息');
    }
  });

  // --- 截图导出：合成多层画布并下载 ---
  ui.downloadShot.addEventListener('click', () => {
    try {
      const composite = document.createElement('canvas');
      composite.width = width; composite.height = height;
      const cctx = composite.getContext('2d');

      // 1) 绘制自研主画布
      cctx.drawImage(canvas, 0, 0, width, height);

      // 2) 绘制 fireworks-js 画布（若可见）
      if (fwjs.container.style.display !== 'none') {
        const fwCanvas = fwjs.container.querySelector('canvas');
        if (fwCanvas) cctx.drawImage(fwCanvas, 0, 0, width, height);
      }

      // 3) 绘制 tsParticles 画布（若可见）
      if (tsp.container.style.display !== 'none') {
        const tspCanvas = tsp.container.querySelector('canvas');
        if (tspCanvas) cctx.drawImage(tspCanvas, 0, 0, width, height);
      }

      // 4) 绘制叠层文案（近似样式）
      if (!ui.overlay.classList.contains('hidden')) {
        cctx.save();
        cctx.textAlign = 'center';
        cctx.textBaseline = 'middle';
        cctx.font = `bold ${Math.max(28, Math.min(width * 0.06, 72))}px Noto Sans SC, system-ui`;
        cctx.fillStyle = '#ffffff';
        cctx.shadowColor = 'rgba(255, 90, 150, 0.6)';
        cctx.shadowBlur = 20;
        cctx.fillText(ui.overlayText.textContent || '', width/2, height/2);
        cctx.restore();
      }

      const a = document.createElement('a');
      a.href = composite.toDataURL('image/png');
      a.download = 'fireworks-confession.png';
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
    } catch (e) {
      console.error('截图导出失败', e);
      alert('抱歉，截图失败。请重试或更换浏览器。');
    }
  });
})();